RecyclerView with header and footer

最近在实现图1这个设置页面的时候,第一眼看到设计稿的时候只是想着普通的LinearLayout加一些常见的ui控件就足够了,但是细想一下觉得这样布局里面重复的代码太多,而且后面要是再多添加几项设置或者删除某项设置,改动也挺大的,想着用RecyclerView带上header合footer实现起来更加合理,于是就使用RecyclerView实现了。

在用带header和footer的RecyclerView实现之前必须先确定好header和footer的布局,我是这样分类header和footer的,见图:

之所以把记录提醒也算到header里面而不是普通的一个item里面是因为记录提醒这个设置项点击开启之后,会在其下方出来一个提示时间的显示和修改的布局,当然这并不是说不能把记录提醒当做一个item处理,完全可以的,我只是偷点懒而已

那么选择好header和footer之后,接下来就是编程实现了,这里主要是适配器的代码来控制header,item和footer,所以只贴出适配器的代码,其他在activity为RecyclerView设置LayoutManager和适配器这部分代码都很简单了,适配器代码如下,部分地方已标注:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
public class SettingAdapter extends RecyclerView.Adapter {

private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private static final int TYPE_FOODER = 2;

private Context mContext;
private List mList;

public SettingAdapter(Context context, List list) {
L.d("SettingAdapter Construction list size = " + list.size());
mContext = context;
mList = list;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View item = LayoutInflater.from(mContext)
.inflate(R.layout.layout_settings_item, parent, false);
return new SettingItemVH(item);
} else if (viewType == TYPE_HEADER) {
View header = LayoutInflater.from(mContext)
.inflate(R.layout.layout_settings_header, parent, false);
return new SettingHeaderVH(header);
} else if (viewType == TYPE_FOODER) {
View footer = LayoutInflater.from(mContext)
.inflate(R.layout.layout_settings_footer, parent, false);
return new SettingFooterVH(footer);
}

throw new RuntimeException("there is no type matches the type " + viewType);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof SettingItemVH) {
SettingItem settingItem = getItem(position);

((SettingItemVH) holder).mIcon.setImageResource(settingItem.getIcon());
((SettingItemVH) holder).mTitle.setText(settingItem.getTitle());
} else if (holder instanceof SettingHeaderVH) {

}

}

@Override
public int getItemCount() {
// 因为多了header和footer这里要+2
return mList.size() + 2;
}

//根据position获取ViewType
@Override
public int getItemViewType(int position) {
if (isHeader(position))
return TYPE_HEADER;
else if (isFooter(position))
return TYPE_FOODER;
return TYPE_ITEM;
}

//第一个item是header
private boolean isHeader(int position) {
return position == 0;
}

//最后一个item是footer
private boolean isFooter(int position) {
return position == getItemCount()-1;
}

//正常的item是从1开始的,0是header,所以获取正常item的时候是position-1
private SettingItem getItem(int position) {
return mList.get(position - 1);
}

class SettingItemVH extends RecyclerView.ViewHolder {

private ImageView mIcon;
private TextView mTitle;

public SettingItemVH(View itemView) {
super(itemView);
mIcon = (ImageView) itemView.findViewById(R.id.setting_item_icon);
mTitle = (TextView) itemView.findViewById(R.id.setting_item_title);
}
}

class SettingHeaderVH extends RecyclerView.ViewHolder {

public SettingHeaderVH(View itemView) {
super(itemView);
}
}

class SettingFooterVH extends RecyclerView.ViewHolder {

public SettingFooterVH(View itemView) {
super(itemView);
}
}
}

这里需要注意的就是getItem的时候是position-1,因为设置item是从第一个开始的,第0个被header占用了,为了获取传进来的list中的索引为0的设置item,就要用position-1